Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the logpdf of NegativeBinomial #1583

Merged
merged 8 commits into from
Sep 17, 2022
Merged

Conversation

devmotion
Copy link
Member

@devmotion devmotion commented Jul 4, 2022

Uses xlogy and xlog1py now which take care of the special case p == 1 automatically. Moreover, for improved numerical accuracy for all cases where k == 0 (and a bit improved efficiency) there's a new branch for k == 0.

Will fix #1582 once the rules for xlogy and xlog1py in DiffRules and LogExpFunctions are fixed (will open PRs).

Edit: Fixes #1582.

@codecov-commenter
Copy link

codecov-commenter commented Jul 4, 2022

Codecov Report

Base: 85.60% // Head: 85.94% // Increases project coverage by +0.33% 🎉

Coverage data is based on head (cc5b013) compared to base (371a427).
Patch coverage: 100.00% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1583      +/-   ##
==========================================
+ Coverage   85.60%   85.94%   +0.33%     
==========================================
  Files         127      129       +2     
  Lines        8044     8080      +36     
==========================================
+ Hits         6886     6944      +58     
+ Misses       1158     1136      -22     
Impacted Files Coverage Δ
src/univariate/discrete/negativebinomial.jl 94.73% <100.00%> (ø)
src/Distributions.jl 100.00% <0.00%> (ø)
src/samplers.jl 100.00% <0.00%> (ø)
src/quantilealgs.jl 81.63% <0.00%> (+0.18%) ⬆️
src/common.jl 80.61% <0.00%> (+0.19%) ⬆️
src/multivariate/dirichlet.jl 77.91% <0.00%> (+0.40%) ⬆️
src/matrixvariates.jl 95.65% <0.00%> (+0.41%) ⬆️
src/multivariate/multinomial.jl 85.71% <0.00%> (+1.36%) ⬆️
src/samplers/multinomial.jl 84.61% <0.00%> (+2.56%) ⬆️
src/univariates.jl 74.07% <0.00%> (+2.64%) ⬆️
... and 3 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@simsurace
Copy link
Contributor

Will fix #1582

Wouldn't it make sense to add a test for this now?

@devmotion
Copy link
Member Author

It's not fixed by the PR alone, so the test would fail.

@simsurace
Copy link
Contributor

@simsurace
Copy link
Contributor

Now that JuliaDiff/DiffRules.jl#85 and JuliaStats/LogExpFunctions.jl#57 have been merged, this could be revived, right?

@devmotion devmotion force-pushed the dw/logpdf_negbinomial branch from 1798647 to da74c00 Compare August 24, 2022 14:54
@devmotion devmotion marked this pull request as ready for review August 24, 2022 15:26
@devmotion devmotion requested a review from matbesancon August 25, 2022 11:52
@devmotion
Copy link
Member Author

FYI I applied similar changes also to the reverse-mode rule, and moved computations of constants outside of the pullback. That leads to a significant performance improvement of the pullback function (of course, only matters if it is evaluated multiple times):

On master

julia> using Distributions, ChainRulesCore, BenchmarkTools

julia> d = NegativeBinomial(0.8, 0.5);

julia> x = 1;

julia> @btime rrule($logpdf, $d, $x);
  45.421 ns (0 allocations: 0 bytes)

julia> _, pb = rrule(logpdf, d, x);

julia> @btime $pb($(randn()));
  36.472 ns (0 allocations: 0 bytes)

With this PR

julia> using Distributions, ChainRulesCore, BenchmarkTools

julia> d = NegativeBinomial(0.8, 0.5);

julia> x = 1;

julia> @btime rrule($logpdf, $d, $x);
  77.026 ns (0 allocations: 0 bytes)

julia> _, pb = rrule(logpdf, d, x);

julia> @btime $pb($(randn()));
  1.501 ns (0 allocations: 0 bytes)

r, p = params(d)
z = xlogy(r, p) + xlog1py(k, -p)
if iszero(k)
# in this case `log(k + r) + logbeta(r, k + 1) == 0` analytically but unfortunately not numerically
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add another comment line on why this leads to returning z?

if iszero(k)
# in this case `log(k + r) + logbeta(r, k + 1) == 0` analytically but unfortunately not numerically
return z
elseif insupport(d, k)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a preference for early termination with edge cases and finishing with the standard case (as was done before, so a branch for !insupport and the normal computation otherwise

@matbesancon
Copy link
Member

two minor comments, shouldn't be a big deal

@devmotion
Copy link
Member Author

I tried to address your comments but I was not completely sure what you had in mind. Can you check if I changed it correctly?

@devmotion
Copy link
Member Author

I'd like to get this in, so I'll merge since I think I addressed the two comments. I'm happy to open a follow-up PR if something could/should be improved.

@devmotion devmotion merged commit c6ef060 into master Sep 17, 2022
@devmotion devmotion deleted the dw/logpdf_negbinomial branch September 17, 2022 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ForwardDiff of logpdf of NegativeBinomial gives wrong results for p==1
4 participants